<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="反射1">
<meta name="twitter:description" content="<p>为什么使用反射；reflect.Type和reflect.Value：Kind方法；递归的值显示器：分析各类型分支，使用示例，循环引用；编码 S表达式：编码方式，编码器实现</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gopl-reflection-1.html"/>
<meta property="og:title" content="来玩魔王的咚 | 反射1" />
<meta property="og:description" content="<p>为什么使用反射；reflect.Type和reflect.Value：Kind方法；递归的值显示器：分析各类型分支，使用示例，循环引用；编码 S表达式：编码方式，编码器实现</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>为什么使用反射；reflect.Type和reflect.Value：Kind方法；递归的值显示器：分析各类型分支，使用示例，循环引用；编码 S表达式：编码方式，编码器实现</p>" />

    <meta name="tags" content="gopl" />
    <meta name="tags" content="反射" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-08-13 17:00:00+08:00">
                <script>document.write(moment('2020-08-13 17:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-08-13 17:00:00+08:00">
                <script>document.write(moment('2020-08-13 17:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            反射1
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#wei-shen-me-shi-yong-fan-she">为什么使用反射</a><ul>
<li><a href="#ge-shi-hua-han-shu">格式化函数</a></li>
</ul>
</li>
<li><a href="#reflecttype-he-reflectvalue">reflect.Type 和 reflect.Value</a><ul>
<li><a href="#type">Type</a></li>
<li><a href="#value">Value</a></li>
<li><a href="#ge-shi-hua-han-shu_1">格式化函数</a></li>
</ul>
</li>
<li><a href="#display-yi-ge-di-gui-de-zhi-xian-shi-qi">Display：一个递归的值显示器</a><ul>
<li><a href="#fen-xi-lei-xing-fen-zhi">分析类型分支</a></li>
<li><a href="#shi-ji-shi-yong">实际使用</a></li>
<li><a href="#xun-huan-yin-yong">循环引用</a></li>
</ul>
</li>
<li><a href="#shi-li-bian-ma-s-biao-da-shi">示例：编码 S表达式</a><ul>
<li><a href="#bian-ma-fang-shi">编码方式</a></li>
<li><a href="#bian-ma-qi-shi-xian">编码器实现</a></li>
</ul>
</li>
</ul>
</div>
<p>Go 语言提供了一种机制，在编译时不知道类型的情况下，可更新变量、在运行时查看值、调用方法以及直接对它们的布局进行操作，这种机制称为<strong>反射</strong>（reflection）。  </p>
<p>这篇整理的还是有点难以理解，不过使用示例还是非常值得参考的。我又写了一篇，这次我写的时候对反射的理解比原来更清晰了。建议先去那篇搞明白反射，之后再看这篇的示例参考写反射的代码：<a href="/gotour-reflection.html">gotour运行时反射</a>  </p>
<p><strong>本篇各章节的主要内容：</strong>  </p>
<ol>
<li>为什么使用反射：不使用反射会有哪些限制</li>
<li>reflect 包的两个主要类型：通过反射获取基础类型的值</li>
<li>递归地获取组合类型的值</li>
<li>示例，编码 S 表达式：以上内容的综合运用</li>
</ol>
<p>关于反射的文章，下面这篇也不错的，条条理比较清晰，可以参考。<br>
Go语言基础之反射： <a href="https://www.liwenzhou.com/posts/Go/13_reflect/">https://www.liwenzhou.com/posts/Go/13_reflect/</a></p>
<h3 id="wei-shen-me-shi-yong-fan-she"><a class="toclink" href="#wei-shen-me-shi-yong-fan-she">为什么使用反射</a></h3>
<p>有时候我们需要编写一个函数，一个有能力统一处理各种值类型的函数。而这些类型可能无法共享同一个接口，也可能布局未知，还有可能这个类型在设计函数的时候还不存在。甚至这个类型会同时存在以上多个或全部的问题。  </p>
<h4 id="ge-shi-hua-han-shu"><a class="toclink" href="#ge-shi-hua-han-shu">格式化函数</a></h4>
<p>一个熟悉的例子是 fmt.Printf 中的格式化逻辑，它可以输出任意类型的任意值，包括用户自定义的类型。下面尝试写一个与 fmt.Sprint 类似的函数，只接收一个值然后返回字符串，函数名就称为 Sprint。<br>
先用一个类型分支来判断这个参数是否定义了 String 方法，如果有就调用它。然后添加一些 switch 分支来判断参数的动态类型是否是基本类型，再对每种类型采用不同的格式化操作：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">Sprint</span><span class="p">(</span><span class="nx">x</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">type</span><span class="w"> </span><span class="nx">stringer</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">x</span><span class="p">.(</span><span class="kd">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">stringer</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="p">.</span><span class="nx">String</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="kt">string</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="kt">int</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">Itoa</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// ...similar cases for int16, uint32, and so on...</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="kt">bool</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;true&quot;</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;false&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="c1">// array, chan, func, map, pointer, slice, struct</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;???&quot;</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>到此，还没有用到反射。<br>
对于复合数据类型，也可以添加更多的分支。但是比如数组，不用的长度就是不一样的类型，所以这样的类型有无限多。另外还有自定义命名的类型。当我们无法透视一个未知类型的布局时，这段代码就无法继续，现在就需要反射了。  </p>
<h3 id="reflecttype-he-reflectvalue"><a class="toclink" href="#reflecttype-he-reflectvalue">reflect.Type 和 reflect.Value</a></h3>
<p>反射功能由 reflect 包提供，它定义了两个重要的类型：</p>
<ul>
<li>Type ：</li>
<li>Value ：</li>
</ul>
<h4 id="type"><a class="toclink" href="#type">Type</a></h4>
<p>reflect.Type 是一个接口，每个 Type 表示 Go 语言的一个类型。<br>
reflect.TypeOf 函数接受 interface{} 参数，以 reflect.Type 的形式返回动态类型：</p>
<div class="highlight"><pre><span></span><code><span class="nx">t</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="w">  </span><span class="c1">// a reflect.Type</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"> </span><span class="c1">// &quot;int&quot;</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span><span class="w">          </span><span class="c1">// &quot;int&quot;</span><span class="w"></span>
</code></pre></div>

<p>因为 reflect.TypeOf 返回一个接口值对应的动态类型，所以它返回的总是具体类型而不是接口类型：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">w</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">w</span><span class="p">))</span><span class="w"> </span><span class="c1">// &quot;*os.File&quot;</span><span class="w"></span>
</code></pre></div>

<p>因为输出一个接口值的动态类型在调试和日志中很常用，所以 fmt.Printf 提供了一个简单的方式 %T，内部的实现就是 reflect.TypeOf：</p>
<div class="highlight"><pre><span></span><code><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="c1">// &quot;int&quot;</span><span class="w"></span>
</code></pre></div>

<h4 id="value"><a class="toclink" href="#value">Value</a></h4>
<p>reflect.Value 是一个结构体类型，可以包含一个任意类型的值。<br>
reflect.ValueOf 函数接受 interface{} 参数，将接口的动态值以 reflect.Value 的形式返回。与 reflect.TypeOf 类似，reflect.Value 返回的结果也是具体类型，不过也可以是一个接口值：</p>
<div class="highlight"><pre><span></span><code><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="c1">// a reflect.Value</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span><span class="w">          </span><span class="c1">// &quot;3&quot;</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">)</span><span class="w">   </span><span class="c1">// &quot;3&quot;</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"> </span><span class="c1">// NOTE: &quot;&lt;int Value&gt;&quot;</span><span class="w"></span>
</code></pre></div>

<p>reflect.Value 也满足 fmt.Stringer，但除非 Value 包含的是一个字符串，否则 String 方法的结果仅仅暴露类型。通常，需要 fmt 包的 %v 功能，它会对 reflect.Value 进行特殊处理。  </p>
<h5 id="value-jie-gou-ti-de-fang-fa"><a class="toclink" href="#value-jie-gou-ti-de-fang-fa">Value 结构体的方法</a></h5>
<p>调用 Value 的 Type 方法会把它的类型以 reflect.Type 方式返回：</p>
<div class="highlight"><pre><span></span><code><span class="nx">t</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">()</span><span class="w">           </span><span class="c1">// a reflect.Type</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"> </span><span class="c1">// &quot;int&quot;</span><span class="w"></span>
</code></pre></div>

<p>reflect.ValueOf 的逆操作是 reflect.Value.Interface 方法。它返回一个 interface{}，即空接口值，与 reflect.Value 包含同一个具体值：</p>
<div class="highlight"><pre><span></span><code><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="c1">// a reflect.Value</span><span class="w"></span>
<span class="nx">x</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Interface</span><span class="p">()</span><span class="w">      </span><span class="c1">// an interface{}</span><span class="w"></span>
<span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">x</span><span class="p">.(</span><span class="kt">int</span><span class="p">)</span><span class="w">            </span><span class="c1">// an int</span><span class="w"></span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%d\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">)</span><span class="w">   </span><span class="c1">// &quot;3&quot;</span><span class="w"></span>
</code></pre></div>

<p>reflect.Value 和 interface{} 都可以包含任意的值。二者的区别是空接口隐藏了值的布局信息、内置操作和相关方法，所以除非知道它的动态类型，并用一个类型断言来滲透进去（就如上面的代码那样），否则对所包含的值能做的事情很少。作为对比，Value 有很多方法可以用来分析所包含的值，而不用知道它的类型。  </p>
<h4 id="ge-shi-hua-han-shu_1"><a class="toclink" href="#ge-shi-hua-han-shu_1">格式化函数</a></h4>
<p>使用反射的技术，第二次尝试写一个通用的格式化函数，这次名称叫： fotmat.Any。<br>
不用类型分支，这里用 reflec.Value 的 Kind 方法来区分不同的类型。尽管有无限种类型，但类型的<strong>分类</strong>（kind）只有少数几种：</p>
<ul>
<li>基础类型：</li>
<li>bool</li>
<li>String</li>
<li>各种数字类型</li>
<li>聚合类型:</li>
<li>Array</li>
<li>Struct</li>
<li>引用类型</li>
<li>chan</li>
<li>Func</li>
<li>Ptr</li>
<li>Slice</li>
<li>Map</li>
<li>接口类型:</li>
<li>interface</li>
</ul>
<p>最后还有一个 Invalid 类型，表示它们还没有任何的值。（reflect.Value 的零值就属于 Invalid 类型。）</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">format</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;reflect&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strconv&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="c1">// Any 把任何值格式化为一个字符串</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">Any</span><span class="p">(</span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">formatAtom</span><span class="p">(</span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="nx">value</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// formatAtom 格式化一个值，且不分析它的内部结构</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">formatAtom</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Kind</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Invalid</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;Invalid&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int64</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatInt</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Int</span><span class="p">(),</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint64</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uintptr</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatUint</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Uint</span><span class="p">(),</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// ... 浮点数和负数的分支省略了...</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Bool</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatBool</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Bool</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">String</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">Quote</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Chan</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Func</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Ptr</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Slice</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Map</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot;0x&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatUint</span><span class="p">(</span><span class="nb">uint64</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Pointer</span><span class="p">()),</span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="c1">// reflect.Array, reflect.Struct, reflect.Interface</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; value&quot;</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>到目前为止，这个函数把每个值作为一个没有内部结构且不可分割的物体（所以函数名称叫formatAtom）。对于聚合类型和接口，只输出值的类型。对于引用类型，输出类型和以十六进制表示的引用地址。这个结构仍然不够理想，下一节会继续改进。  </p>
<p>因为 Kind 只关心底层实现，所以 format. Any 对命名类型的效果也很好：</p>
<div class="highlight"><pre><span></span><code>var x int64 = 1
var d time.Duration = 1 * time.Nanosecond
fmt.Println(format.Any(x))                  // &quot;1&quot;
fmt.Println(format.Any(d))                  // &quot;1&quot;
fmt.Println(format.Any([]int64{x}))         // &quot;[]int64 0x8202b87b0&quot;
fmt.Println(format.Any([]time.Duration{d})) // &quot;[]time.Duration 0x8202b87e0&quot;
</code></pre></div>

<h3 id="display-yi-ge-di-gui-de-zhi-xian-shi-qi"><a class="toclink" href="#display-yi-ge-di-gui-de-zhi-xian-shi-qi">Display：一个递归的值显示器</a></h3>
<p>接下来改善组合类型的显示。这次不再实现一个 fmt.Sprint，而是实现一个称为 Display 的调试工具函数，这个函数对给定的一个复杂值x，输出这个复杂值的完整结构，并对找到的每个元素标上这个元素的路径。<br>
应当尽量避免在包的 API 里暴露反射的相关内容，之后将定义一个未导出的函数 display 来做真正的递归处理，再暴露 Display，而 Display 则只是一个简单的封装：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">Display</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Display %s (%T):\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">display</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="nx">x</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>在 display 中，使用之前定义的 formatAtom 函数来输出基础值，直接就把这个函数搬过来了。使用 reflect. Value 的一些方法来递归展示复杂类型的每个组成部分。当递归深入是，path 字符串会增长，表示是如何达到当前值的。  </p>
<p>上两节的示例都是在模拟实现 fmt.Sprint，结构都是通过 strconv 包转成字符串然后返回的。<br>
这里就直接使用 fmt 包简化了部分逻辑：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">display</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;reflect&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strconv&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="c1">// formatAtom 格式化一个值，且不分析它的内部结构</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">formatAtom</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Kind</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Invalid</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;invalid&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int64</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatInt</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Int</span><span class="p">(),</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint64</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uintptr</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatUint</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Uint</span><span class="p">(),</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// ... 浮点数和负数的分支省略了...</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Bool</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Bool</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;true&quot;</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;false&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">String</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">Quote</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Chan</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Func</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Ptr</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Slice</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Map</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; 0x&quot;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">FormatUint</span><span class="p">(</span><span class="nb">uint64</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">Pointer</span><span class="p">()),</span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="c1">// reflect.Array, reflect.Struct, reflect.Interface</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&quot; value&quot;</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">Display</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Display %s (%T):\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">display</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="nx">x</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">display</span><span class="p">(</span><span class="nx">path</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Kind</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Invalid</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%s = invalid\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Slice</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Array</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Len</span><span class="p">();</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">display</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s[%d]&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">),</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Index</span><span class="p">(</span><span class="nx">i</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Struct</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">NumField</span><span class="p">();</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fieldPath</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s.%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">Field</span><span class="p">(</span><span class="nx">i</span><span class="p">).</span><span class="nx">Name</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">display</span><span class="p">(</span><span class="nx">fieldPath</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Field</span><span class="p">(</span><span class="nx">i</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Map</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">MapKeys</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">display</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s[%s]&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">,</span><span class="w"></span>
<span class="w">                </span><span class="nx">formatAtom</span><span class="p">(</span><span class="nx">key</span><span class="p">)),</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">MapIndex</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Ptr</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">IsNil</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%s = nil\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">display</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;(*%s)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">),</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Elem</span><span class="p">())</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Interface</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">IsNil</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%s = nil\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%s.type = %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Elem</span><span class="p">().</span><span class="nx">Type</span><span class="p">())</span><span class="w"></span>
<span class="w">            </span><span class="nx">display</span><span class="p">(</span><span class="nx">path</span><span class="o">+</span><span class="s">&quot;.value&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Elem</span><span class="p">())</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="c1">// 基本类型、通道、函数</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%s = %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">formatAtom</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="fen-xi-lei-xing-fen-zhi"><a class="toclink" href="#fen-xi-lei-xing-fen-zhi">分析类型分支</a></h4>
<p>接下来对 display 函数里的类型分支逐一进行分析。  </p>
<h5 id="slice-yu-shu-zu"><a class="toclink" href="#slice-yu-shu-zu">slice与数组</a></h5>
<p>两者的逻辑一致。Len 方法返回元素的个数，Index(i) 会返回第 i 个元素，返回的元素的类型为 reflect.Value（如果i越界会崩溃）。这两个方法与内置的 len(a) 和 a[i] 序列操作类型。在每个序列上递归调用了 display 函数，只是在路径后追加了 "[i]"。<br>
尽管 reflect.Value 有很多方法，但对于每个值，只有少量的方法可以安全调用。比如，Index 方法可以在 Slice、Arrar、String 类型的值上安全调用，但对于其他类型则会崩溃。  </p>
<h5 id="jie-gou-ti"><a class="toclink" href="#jie-gou-ti">结构体</a></h5>
<p>NumField 方法可以报告结构中的字段数，Field(i) 会返回第 i 个字段，返回的字段类型为 reflect.Value。字段列表包括了从匿名字段中做了类型提升的字段。<br>
<code>v.Field(i)</code> 是第i个字段的值，<code>v.Type().Field(i)</code> 就是第i个字段的名称，然后再 .name 就是名称的字符串类型。  </p>
<h5 id="map"><a class="toclink" href="#map">map</a></h5>
<p>MapKeys 方法返回一个元素类型为 reflect.Value 的 slice，每个元素都是一个 map 的 key。与平常遍历 map 的结果类似，顺序是不固定的。MapIndex(key) 返回 key 对应的值。这里还是忽略了一些情形，map 的 key 也可能是超出 formatAtom 能处理的合法类型，比如数组、结构体、接口都可以是合法的key。这还需要再修改一点代码，这里就没有做。  </p>
<h5 id="zhi-zhen"><a class="toclink" href="#zhi-zhen">指针</a></h5>
<p>Elem 方法返回指针指向的变量，同样也是以 reflect.Value 类型返回。这个方法在指针是 nil 时也能正确处理，但返回的结果属于 Invalid 类型，所以用了 IsNil 来显式检测空指针，方便输出一条合适的消息。为了避免歧义，在路径前加了 * 外边再套一层圆括号。  </p>
<h5 id="jie-kou"><a class="toclink" href="#jie-kou">接口</a></h5>
<p>再次使用 IsNil 来判断接口是否为空。然后用 v.Elem() 获取接口的动态值。再打印出对应的类型的值。  </p>
<h4 id="shi-ji-shi-yong"><a class="toclink" href="#shi-ji-shi-yong">实际使用</a></h4>
<p>现在 Display 已经完成了，马上就来实际使用一下。<br>
使用下面的这样一个复杂的结构体来进行验证：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="s">&quot;gopl/ch12/display&quot;</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">Movie</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Title</span><span class="p">,</span><span class="w"> </span><span class="nx">Subtitle</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Year</span><span class="w">            </span><span class="kt">int</span><span class="w"></span>
<span class="w">    </span><span class="nx">Color</span><span class="w">           </span><span class="kt">bool</span><span class="w"></span>
<span class="w">    </span><span class="nx">Actor</span><span class="w">           </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Oscars</span><span class="w">          </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Sequel</span><span class="w">          </span><span class="o">*</span><span class="kt">string</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">strangelove</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Movie</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">Title</span><span class="p">:</span><span class="w">    </span><span class="s">&quot;Dr. Strangelove&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Subtitle</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;How I Learned to Stop Worrying and Love the Bomb&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Year</span><span class="p">:</span><span class="w">     </span><span class="mi">1964</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Color</span><span class="p">:</span><span class="w">    </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Actor</span><span class="p">:</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Dr. Strangelove&quot;</span><span class="p">:</span><span class="w">            </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Grp. Capt. Lionel Mandrake&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Pres. Merkin Muffley&quot;</span><span class="p">:</span><span class="w">       </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Gen. Buck Turgidson&quot;</span><span class="p">:</span><span class="w">        </span><span class="s">&quot;George C. Scott&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Brig. Gen. Jack D. Ripper&quot;</span><span class="p">:</span><span class="w">  </span><span class="s">&quot;Sterling Hayden&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">`Maj. T.J. &quot;King&quot; Kong`</span><span class="p">:</span><span class="w">      </span><span class="s">&quot;Slim Pickens&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="p">},</span><span class="w"></span>

<span class="w">        </span><span class="nx">Oscars</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Actor (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Adapted Screenplay (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Director (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Picture (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="p">},</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>

<span class="w">    </span><span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;strangelove&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">strangelove</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>执行后输出如下：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\gopl\ch12\desplay_demo&gt; go run main.go
Display strangelove (main.Movie):
strangelove.Title = &quot;Dr. Strangelove&quot;
strangelove.Subtitle = &quot;How I Learned to Stop Worrying and Love the Bomb&quot;
strangelove.Year = 1964
strangelove.Color = false
strangelove.Actor[&quot;Gen. Buck Turgidson&quot;] = &quot;George C. Scott&quot;
strangelove.Actor[&quot;Brig. Gen. Jack D. Ripper&quot;] = &quot;Sterling Hayden&quot;
strangelove.Actor[&quot;Maj. T.J. \&quot;King\&quot; Kong&quot;] = &quot;Slim Pickens&quot;
strangelove.Actor[&quot;Dr. Strangelove&quot;] = &quot;Peter Sellers&quot;
strangelove.Actor[&quot;Grp. Capt. Lionel Mandrake&quot;] = &quot;Peter Sellers&quot;
strangelove.Actor[&quot;Pres. Merkin Muffley&quot;] = &quot;Peter Sellers&quot;
strangelove.Oscars[0] = &quot;Best Actor (Nomin.)&quot;
strangelove.Oscars[1] = &quot;Best Adapted Screenplay (Nomin.)&quot;
strangelove.Oscars[2] = &quot;Best Director (Nomin.)&quot;
strangelove.Oscars[3] = &quot;Best Picture (Nomin.)&quot;
strangelove.Sequel = nil
PS G:\Steed\Documents\Go\src\gopl\ch12\desplay_demo&gt;
</code></pre></div>

<h5 id="diao-yong-biao-zhun-ku-de-nei-bu-jie-gou"><a class="toclink" href="#diao-yong-biao-zhun-ku-de-nei-bu-jie-gou">调用标准库的内部结构</a></h5>
<p>还可以使用 Display 来显示标准库类型的内部结构，比如： *os.File：</p>
<div class="highlight"><pre><span></span><code><span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;os.Stderr&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>注意，即使是非导出的字段在反射下也是可见的。  </p>
<p>还可以把 Display 作用在 reflect.Value 上，并且观察它如何遍历 *os.File 的类型描述符的内部结构：</p>
<div class="highlight"><pre><span></span><code><span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;rV&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">))</span><span class="w"></span>
</code></pre></div>

<h5 id="diao-yong-zhi-zhen"><a class="toclink" href="#diao-yong-zhi-zhen">调用指针</a></h5>
<p>这里注意如下两个例子的差异：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="kd">interface</span><span class="p">{}</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">3</span><span class="w"></span>

<span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;i&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">)</span><span class="w"></span>
<span class="c1">// 输出:</span><span class="w"></span>
<span class="c1">// Display i (int):</span><span class="w"></span>
<span class="c1">// i = 3</span><span class="w"></span>

<span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;&amp;i&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">i</span><span class="p">)</span><span class="w"></span>
<span class="c1">// 输出:</span><span class="w"></span>
<span class="c1">// Display &amp;i (*interface {}):</span><span class="w"></span>
<span class="c1">// (*&amp;i).type = int</span><span class="w"></span>
<span class="c1">// (*&amp;i).value = 3</span><span class="w"></span>
</code></pre></div>

<p>在第一个例子中，Display 调用 reflect.ValueOf(i)，返回值的类型为 int。<br>
在第二个例子中，Display 调用 reflect.ValueOf(&amp;i)，返回的类型为 Ptr，并且是一个指向i的指针。在 Display 的 Ptr 分支中，会调用 Elem 方法，返回一个代表变量 i 的 Value，其类型为 Interface。类似这种间接获得的 Value 可以代表任何值，包括这里的接口。这是 display 函数递归调用自己，输出接口的动态类型和动态值。  </p>
<h4 id="xun-huan-yin-yong"><a class="toclink" href="#xun-huan-yin-yong">循环引用</a></h4>
<p>在当前的这个实现中，Display 在对象图中存在循环引用时不会自行终止。<br>
比如出现一个首尾相连的链表：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 一个指向自己的结构体</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Cycle</span><span class="w"> </span><span class="kd">struct</span><span class="p">{</span><span class="w"> </span><span class="nx">Value</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span><span class="nx">Tail</span><span class="w"> </span><span class="o">*</span><span class="nx">Cycle</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="nx">Cycle</span><span class="w"></span>
<span class="nx">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">Cycle</span><span class="p">{</span><span class="mi">42</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">c</span><span class="p">}</span><span class="w"></span>
<span class="nx">display</span><span class="p">.</span><span class="nx">Display</span><span class="p">(</span><span class="s">&quot;c&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>执行后会输出一个持续增长的展开式：</p>
<div class="highlight"><pre><span></span><code>Display c (main.Cycle):
c.Value = 42
(*c.Tail).Value = 42
(*(*c.Tail).Tail).Value = 42
(*(*(*c.Tail).Tail).Tail).Value = 42
(*(*(*(*c.Tail).Tail).Tail).Tail).Value = 42
</code></pre></div>

<p>很多 Go 程序都会包含一些循环引用的数据。让 Display 支持这类成环的数据结构需要些技巧，需要额外记录迄今访问的路径，相应会带来成本。<br>
一个通用的解决方案需要 unsafe 语言特性，在之后的 unsafe 包的示例中，会有对循环引用的处理。  </p>
<p>还有一个相对比较容易实现的思路，限制递归的层数。这个不是那么通用，也不是很完美。但是不需要借助 unsafe 就可以实现。  </p>
<p>循环引用在 fmt.Sprint 中不构成一个大问题，因为它很少尝试输出整个结构体。比如，当遇到一个指针时，就只简单地输出指针的数字值，这样就不是引用了。但如果遇到一个 slice 或 map 包含自身，它还是会卡住，只是不值得为了这种罕见的案例而去承担处理循环引用的成本。  </p>
<h3 id="shi-li-bian-ma-s-biao-da-shi"><a class="toclink" href="#shi-li-bian-ma-s-biao-da-shi">示例：编码 S表达式</a></h3>
<p>Display 现在可以作为一个显示结构化数据的调试工具，只要再稍加修改，就可以用它来对任意 Go 对象进行<strong>编码或编排</strong>，使之成为适用于进程间通信的消息。<br>
Go 的标准库已经支持了各种格式，包括：JSON、XML、ASN.1。另外还有一种广泛使用的格式是 Lisp 语言中的 S表达式。与其他格式不同的是 S表达式还没被 Go 标准库支持，主要是因为它没有一个公认的标准规范。<br>
接下来就要定义一个包用于将任意的 Go 对象编码为 S表达式，它需要支持以下的结构：</p>
<div class="highlight"><pre><span></span><code>42          integer
&quot;hello&quot;     string (带有Go风格的引号)
foo         symbol (未用引号括起来的名字)
(1 2 3)     list   (括号包起来的0个或多个元素)
</code></pre></div>

<p>布尔值一般用符号 t 表示真，用空列表 () 或者符号 nil 表示假，但为了简化，这里的实现直接忽略了布尔值。通道和函数也被忽略了，因为它们的状态对于反射来说是不透明的。这里的实现还忽略了实数、复数和接口。（部分实现可以后续进行添加完善。）  </p>
<h4 id="bian-ma-fang-shi"><a class="toclink" href="#bian-ma-fang-shi">编码方式</a></h4>
<p>将 Go 语言的类型编码为S表达式的方法如下：</p>
<ul>
<li>整数和字符串以显而易见的方式编码</li>
<li>空值编码为nil符号</li>
<li>数组和slice被编码为列表</li>
<li>结构体编码为一个字段绑定（field binding）的列表，每个字段绑定都是一个包含两个元素的列表。</li>
<li>map也编码为键值对的列表。按照传统，S表达式使用形式为 (key . value) 的单个结构单元（cons cell）来表示key/value对。但是为了简化解码过程，示例的实现中是没有加 "." 的。</li>
</ul>
<h4 id="bian-ma-qi-shi-xian"><a class="toclink" href="#bian-ma-qi-shi-xian">编码器实现</a></h4>
<p>编码用单个递归调用函数 encode 来实现。它的结构上域上一节的 Display 在本质上是一致的：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">sexpr</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;bytes&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;reflect&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="w"> </span><span class="o">*</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Kind</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Invalid</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteString</span><span class="p">(</span><span class="s">&quot;nil&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Int64</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;%d&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Int</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint8</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint16</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint32</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uint64</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Uintptr</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;%d&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Uint</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">String</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;%q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">String</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Ptr</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Elem</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Array</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Slice</span><span class="p">:</span><span class="w"> </span><span class="c1">// (value ...)</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;(&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Len</span><span class="p">();</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Index</span><span class="p">(</span><span class="nx">i</span><span class="p">));</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;)&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Struct</span><span class="p">:</span><span class="w"> </span><span class="c1">// ((name value) ...)</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;(&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">NumField</span><span class="p">();</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;(%s &quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">().</span><span class="nx">Field</span><span class="p">(</span><span class="nx">i</span><span class="p">).</span><span class="nx">Name</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Field</span><span class="p">(</span><span class="nx">i</span><span class="p">));</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;)&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;)&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Map</span><span class="p">:</span><span class="w"> </span><span class="c1">// ((key value) ...)</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;(&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">MapKeys</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;(&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">MapIndex</span><span class="p">(</span><span class="nx">key</span><span class="p">));</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;)&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">buf</span><span class="p">.</span><span class="nx">WriteByte</span><span class="p">(</span><span class="sc">&#39;)&#39;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="c1">// float, complex, bool, chan, func, interface</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;unsupported type: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">.</span><span class="nx">Type</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// Marshal 把 Go 的值编码为 S 表达式的形式</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">Marshal</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">buf</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">encode</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">ValueOf</span><span class="p">(</span><span class="nx">v</span><span class="p">));</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">buf</span><span class="p">.</span><span class="nx">Bytes</span><span class="p">(),</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>Marshal 函数把上面的编码器封装成一个 API，它类似于其他 encoding/... 包里的 API。  </p>
<p>继续用上一节验证 Display 的结构体来应用到这里：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;gopl/ch12/sexpr&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;os&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">Movie</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">Title</span><span class="p">,</span><span class="w"> </span><span class="nx">Subtitle</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Year</span><span class="w">            </span><span class="kt">int</span><span class="w"></span>
<span class="w">    </span><span class="c1">// Color           bool</span><span class="w"></span>
<span class="w">    </span><span class="nx">Actor</span><span class="w">           </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Oscars</span><span class="w">          </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Sequel</span><span class="w">          </span><span class="o">*</span><span class="kt">string</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">strangelove</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Movie</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">Title</span><span class="p">:</span><span class="w">    </span><span class="s">&quot;Dr. Strangelove&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Subtitle</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;How I Learned to Stop Worrying and Love the Bomb&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Year</span><span class="p">:</span><span class="w">     </span><span class="mi">1964</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="c1">// Color:    false,</span><span class="w"></span>
<span class="w">        </span><span class="nx">Actor</span><span class="p">:</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Dr. Strangelove&quot;</span><span class="p">:</span><span class="w">            </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Grp. Capt. Lionel Mandrake&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Pres. Merkin Muffley&quot;</span><span class="p">:</span><span class="w">       </span><span class="s">&quot;Peter Sellers&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Gen. Buck Turgidson&quot;</span><span class="p">:</span><span class="w">        </span><span class="s">&quot;George C. Scott&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Brig. Gen. Jack D. Ripper&quot;</span><span class="p">:</span><span class="w">  </span><span class="s">&quot;Sterling Hayden&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">`Maj. T.J. &quot;King&quot; Kong`</span><span class="p">:</span><span class="w">      </span><span class="s">&quot;Slim Pickens&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="p">},</span><span class="w"></span>

<span class="w">        </span><span class="nx">Oscars</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Actor (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Adapted Screenplay (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Director (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="s">&quot;Best Picture (Nomin.)&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="p">},</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>

<span class="w">    </span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sexpr</span><span class="p">.</span><span class="nx">Marshal</span><span class="p">(</span><span class="nx">strangelove</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;sexpr.Marshal err: %v&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">b</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>由于现在不支持布尔值，所以会返回错误：</p>
<div class="highlight"><pre><span></span><code>PS H:\Go\src\gopl\ch12\sexpr_demo&gt; go run main.go
sexpr.Marshal err: unsupported type: bool[]
</code></pre></div>

<p>去掉结构体和数据中的Color字段后就正常了：</p>
<div class="highlight"><pre><span></span><code>PS H:\Go\src\gopl\ch12\sexpr_demo&gt; go run main.go
((Title &quot;Dr. Strangelove&quot;) (Subtitle &quot;How I Learned to Stop Worrying and Love the Bomb&quot;) (Year 1964) (Actor ((&quot;Dr. Strangelove&quot; &quot;Peter Sellers&quot;) (&quot;Grp. Capt. Lionel Mandrake&quot; &quot;Peter Sellers&quot;) (&quot;Pres. Merkin Muffley&quot; &quot;Peter Sellers&quot;) (&quot;Gen. Buck Turgidson&quot; &quot;George C. Scott&quot;) (&quot;Brig. Gen. Jack D. Ripper&quot; &quot;Sterling Hayden&quot;) (&quot;Maj. T.J. \&quot;King\&quot; Kong&quot; &quot;Slim Pickens&quot;))) (Oscars (&quot;Best Actor (Nomin.)&quot; &quot;Best Adapted Screenplay (Nomin.)&quot; &quot;Best Director (Nomin.)&quot; &quot;Best Picture (Nomin.)&quot;)) (Sequel nil))
PS H:\Go\src\gopl\ch12\sexpr_demo&gt;
</code></pre></div>

<p>输出的内容非常紧凑，不适合阅读，不过作为格式化的编码已经实现了。<em>如果要输出一个带缩进和换行的美化的格式，要重新实现一个 encode 函数。</em><br>
与 fmt.Print、json.Marshal、Display 这些一样，sexpr.Marshal 在遇到循环引用的数据时也会无限循环。  </p>
<p>接下来还可以继续实现一个解碼器。不过在那之前，还要先了解一下如何用反射来更新程序中的变量。都在下一篇里。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>